/***************************************************************************
 *   Copyright (C) 2008 by Chris Bornholdt   *
 *   chris.bornholdt@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef _OBJECT_SLOT_MAPPER_HPP_
#define _OBJECT_SLOT_MAPPER_HPP_

#include <QObject>
#include <QGenericArgument>
#include <QList>
#include <QtDebug>
#include <tr1/functional>
#include <QVariant>

class ObjectSlotMapper : public QObject
{
	Q_OBJECT
	public:
		typedef std::tr1::function<QObject*()> ObjectSelector;
		
		enum WarningLevel { NoWarning, Warning, Debug };
	
		ObjectSlotMapper(ObjectSelector selector = ObjectSelector(), QObject *parent = 0,
			WarningLevel warningLevel = NoWarning)
			: QObject(parent), objectSelector_(selector), warningLevel_(warningLevel) {}
		~ObjectSlotMapper()
		{
		}
		
		ObjectSelector objectSelector() const
		{	return objectSelector_;	}
		
		void setObjectSelector(ObjectSelector selector)
		{	objectSelector_ = selector;	}
		
		QObject *currentObject() const
		{	return objectSelector_();	}
		
		template<typename T>
		void addMapping(QObject *sender, char const *slot, T arg)
		{	
			mappings_.insert(sender, std::make_pair(slot, QVariant::fromValue(arg)));
			connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(removeMappings(QObject *)));
		}
		
		void addMapping(QObject *sender, char const *slot)
		{
			mappings_.insert(sender, std::make_pair(slot, QVariant()));
			connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(removeMappings(QObject *)));
		}
		
	public slots:
		void clear()	{	mappings_.clear();	}
		
		void removeMappings(QObject *obj)
		{	
			obj->disconnect(this);
			mappings_.remove(obj);	
		}
		
	private slots:
		void map()
		{
			QObject *s = sender();
			QList<std::pair<const char *, QVariant> > connectionList = mappings_.values(s);
			
			QObject *r = 0;
			if(connectionList.count())
				r = objectSelector_();
				
			QList<std::pair<const char *, QVariant> >::const_iterator iter = connectionList.begin();
			for(QGenericArgument arg; iter != connectionList.end(); ++iter)
			{
				arg = iter->second.isValid() ? QGenericArgument(iter->second.typeName(), 
					iter->second.data()) : QGenericArgument();
				if (!QMetaObject::invokeMethod(r, iter->first, arg))
				{
					switch(warningLevel_)
					{
						case NoWarning	:	return;
						case Warning	:
							qWarning() << "failed to connect to " << iter->first;
							break;
						case Debug		:
							qDebug() << "failed to connect to " << iter->first;
					};
				}
			}
		}
		
	private:
		ObjectSelector objectSelector_;
		WarningLevel warningLevel_;
		
		QMultiMap<QObject *, std::pair<const char *, QVariant> > mappings_;
};

#endif
